package com.asgard.crypto;

import com.google.common.primitives.Ints;
import com.google.protobuf.ByteString;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.util.Arrays;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * 该类是对SHA-256格式的哈希的封装。
 */
public class HashBytes implements Comparable<HashBytes>, Hashable {
    private static final int HASH_LENGTH = 256 / 8;

    /**
     * 哈希的原始字节数组
     */
    private final byte[] bytes;

    /**
     * 零哈希
     */
    public static final HashBytes ZERO_HASH = wrap(new byte[HASH_LENGTH]);

    /**
     * 构造函数。
     *
     * @param rawHashBytes 哈希的原始字节数组
     */
    protected HashBytes(byte[] rawHashBytes) {
        checkArgument(rawHashBytes.length == HASH_LENGTH);
        this.bytes = rawHashBytes;
    }

    /**
     * 构造函数。
     *
     * @param hexString Hex格式表示的哈希
     * @throws DecoderException Hex格式错误
     */
    public HashBytes(String hexString) throws DecoderException {
        if (hexString.length() != HASH_LENGTH * 2)
            throw new DecoderException();
        this.bytes = Hex.decode(hexString);
    }

    /**
     * 构造哈希。
     *
     * @param rawHashBytes 原始字节数组
     * @return 哈希
     */
    public static HashBytes wrap(byte[] rawHashBytes) {
        return new HashBytes(rawHashBytes);
    }

    /**
     * 构造哈希。
     *
     * @param rawHashBytes 原始字节数组，采用{@link ByteString}格式表示
     * @return 哈希
     */
    public static HashBytes wrap(ByteString rawHashBytes) {
        return new HashBytes(rawHashBytes.toByteArray());
    }

    /**
     * 构造哈希。
     *
     * @param hexString Hex格式表示的哈希
     * @return 哈希
     * @throws DecoderException Hex格式错误
     */
    public static HashBytes wrap(String hexString) throws DecoderException {
        return new HashBytes(hexString);
    }

    /**
     * 构造哈希，忽略Hex格式错误。
     *
     * @param hexString Hex格式表示的哈希
     * @return 哈希
     */
    public static HashBytes wrapIgnore(String hexString) {
        try {
            return new HashBytes(hexString);
        } catch (DecoderException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 构造反字节序的字节数组表示的哈希。
     *
     * @param rawHashBytes 字节数组，字节序与哈希相反
     * @return 哈希
     */
    public static HashBytes wrapReversed(byte[] rawHashBytes) {
        byte[] newBytes = ArrayUtils.clone(rawHashBytes);
        ArrayUtils.reverse(newBytes);
        return wrap(newBytes);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        return Arrays.equals(bytes, ((HashBytes) o).bytes);
    }

    @Override
    public int hashCode() {
        return Ints.fromBytes(bytes[28], bytes[29], bytes[30], bytes[31]);
    }

    @Override
    public String toString() {
        return Hex.toHexString(bytes);
    }

    /**
     * 将哈希转换为{@link BigInteger}表示的大数。
     *
     * @return 大数
     */
    public BigInteger toBigInteger() {
        return new BigInteger(1, bytes);
    }

    /**
     * 获取哈希对应的字节数组。
     *
     * @return 哈希对应的字节数组
     */
    public byte[] getBytes() {
        return bytes;
    }

    /**
     * 获取反字节序的字节数组。
     *
     * @return 反字节序的字节数组
     */
    public byte[] getReversedBytes() {
        byte[] newbytes = ArrayUtils.clone(bytes);
        ArrayUtils.reverse(newbytes);
        return newbytes;
    }

    @Override
    public int compareTo(HashBytes o) {
        return new CompareToBuilder().append(this.bytes, o.bytes).toComparison();
    }

    /**
     * 随机生成哈希值。
     *
     * @return 随机哈希值
     */
    public static HashBytes randomHash() {
        return HashBytes.wrap(RandomUtils.nextBytes(32));
    }

    @Override
    public HashBytes getHash() {
        return this;
    }

    /**
     * 将哈希值转换为{@link ByteString}格式的字节数组。
     *
     * @return 字节数组
     */
    public ByteString toByteString() {
        return ByteString.copyFrom(bytes);
    }

}
